﻿using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

namespace BToolkit
{
    public class EventManager : MonoBehaviour
    {
        private static EventManager instance;
        private static Queue<Action> eventQueue = new Queue<Action>();
        private static Dictionary<Type, ArrayList> listeners = new Dictionary<Type, ArrayList>();
        private static int mainThreadID;

        void Awake()
        {
            mainThreadID = Thread.CurrentThread.ManagedThreadId;
        }

        void Update()
        {
            while (eventQueue.Count > 0)
            {
                eventQueue.Dequeue().Invoke();
            }
        }

        public static void Add<T>(Action<T> listener) where T : struct
        {
            if (!instance)
            {
                instance = GameObject.FindObjectOfType<EventManager>();
                if (!instance)
                {
                    GameObject go = new GameObject("[EventManager]");
                    DontDestroyOnLoad(go);
                    instance = go.AddComponent<EventManager>();
                }
            }

            Type type = typeof(T);
            ArrayList actions = null;
            if (listeners.ContainsKey(type))
            {
                actions = listeners[type];
            }
            else
            {
                actions = new ArrayList();
                listeners.Add(type, actions);
            }
            actions.Add(listener);
        }

        public static void Remove<T>(Action<T> listener) where T : struct
        {
            Type type = typeof(T);
            if (listeners.ContainsKey(type))
            {
                ArrayList actions = listeners[type];
                if (actions.Contains(listener))
                {
                    actions.Remove(listener);
                }
            }
        }

        public static void Remove<T>() where T : struct
        {
            Type type = typeof(T);
            if (listeners.ContainsKey(type))
            {
                listeners.Remove(type);
            }
        }

        public static void Clear()
        {
            listeners.Clear();
        }

        public static void Emit<T>(T msg) where T : struct
        {
            if (Thread.CurrentThread.ManagedThreadId != mainThreadID)
            {
                eventQueue.Enqueue(() =>
                {
                    Emit(msg);
                });
                return;
            }

            Type type = typeof(T);
            if (listeners.ContainsKey(type))
            {
                ArrayList actions = listeners[type];
                for (int i = 0; i < actions.Count; i++)
                {
                    if (actions[i] != null)
                    {
                        (actions[i] as Action<T>).Invoke(msg);
                    }
                }
            }
        }
    }
}
